package two;

import java.util.*;

/**
 * 该代码中，ratingsData是一个Map<Integer, Map<String, Integer>>类型的变量，
 * 表示每个用户对图书的评分情况，其中键为用户ID，
 * 值为另一个Map<String, Integer>类型的变量，表示该用户给每本图书的评分。
 * similarity函数计算两个用户之间的相似度，采用余弦相似度计算，
 * 并返回一个double类型的相似度值。recommendBooks函数接受一个用户ID，
 * 计算推荐候选集，然后按推荐度从高到低进行排序，并返回一个推荐的图书列表。
 * 需要注意的是，该算法也存在一些问题，例如新用户的冷启动问题、对于长尾问题的解决方案等。
 * 因此，需要结合其他算法和策略进行优化。
 */
public class BookRecommendationSystem2 {
    private Map<Integer, Map<String, Integer>> ratingsData;

    public BookRecommendationSystem2(Map<Integer, Map<String, Integer>> ratingsData) {
        this.ratingsData = ratingsData;
    }

    // 计算两个用户的相似度（采用余弦相似度计算）
    private double similarity(int userId1, int userId2) {
        // 得到user1 与 user2 的评分表
        Map<String, Integer> user1Ratings = ratingsData.get(userId1);
        Map<String, Integer> user2Ratings = ratingsData.get(userId2);

        double dotProduct = 0.0, norm1 = 0.0, norm2 = 0.0;

        for (Map.Entry<String, Integer> entry : user1Ratings.entrySet()) {
            String book = entry.getKey();
            int user1Rating = entry.getValue();
            // user1 与 user2 都评价了这本书
            if (user2Ratings.containsKey(book)) {
                int user2Rating = user2Ratings.get(book);
                dotProduct += user1Rating * user2Rating;
            }
            norm1 += user1Rating * user1Rating;
        }

        for (Map.Entry<String, Integer> entry : user2Ratings.entrySet()) {
            int user2Rating = entry.getValue();
            norm2 += user2Rating * user2Rating;
        }

        return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
    }

    // 给用户推荐图书
    public List<String> recommendBooks(int userId) {
        Map<String, Double> candidateItems = new HashMap<>();
        // 去除当前用户
        Set<Integer> otherUsers = new HashSet<>(ratingsData.keySet());
        otherUsers.remove(userId);

        // 遍历其他用户
        for (int otherUser : otherUsers) {
            double similarityScore = similarity(userId, otherUser);
            // 如果相似度为0，则跳过
            if (similarityScore <= 0.0) {
                continue;
            }

            Map<String, Integer> otherUserRatings = ratingsData.get(otherUser);

            for (Map.Entry<String, Integer> entry : otherUserRatings.entrySet()) {
                String book = entry.getKey();
                int rating = entry.getValue();
                // user1 为评价的 则加入
                if (!ratingsData.get(userId).containsKey(book)) {
                    candidateItems.put(book,
                            candidateItems.getOrDefault(book, 0.0) + rating * similarityScore);
                }
            }
        }

        // 按推荐度排序
        List<String> sortedBooks = new ArrayList<>(candidateItems.keySet());
        sortedBooks.sort((book1, book2) -> candidateItems.get(book2).compareTo(candidateItems.get(book1)));

        return sortedBooks;
    }
}
